package com.ds.infra.ibatis.transaction.interceptor;

import com.ds.infra.ibatis.transaction.Transaction;
import com.ds.infra.ibatis.transaction.TransactionIsolationLevel;
import com.ds.infra.ibatis.transaction.annotation.Isolation;
import com.ds.infra.ibatis.transaction.annotation.Propagation;
import com.ds.infra.ibatis.transaction.annotation.Transactional;
import com.ds.infra.ibatis.transaction.jdbc.JdbcTransactionFactory;
import com.ds.infra.ibatis.transaction.support.TransactionSynchronizationManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

import javax.sql.DataSource;
import java.lang.reflect.Method;
import java.sql.SQLException;

@Slf4j
@RequiredArgsConstructor
public class TransactionInterceptor implements MethodInterceptor {

    private final DataSource dataSource;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Transactional transactional = method.getAnnotation(Transactional.class);

        if (transactional == null) {
            return invocation.proceed();
        }

        Transaction transaction = null;
        try {
            transaction = beginTransaction(transactional);
            Object result = invocation.proceed();
            commitTransaction(transaction);
            return result;
        } catch (Exception e) {
            rollbackTransaction(transaction, e);
            throw e;
        } finally {
            cleanupTransaction(transaction);
        }
    }

    private Transaction beginTransaction(Transactional transactional) throws SQLException {
        if (TransactionSynchronizationManager.getTransaction() != null) {
            // 处理事务传播行为
            if (transactional.propagation() == Propagation.REQUIRED) {
                return TransactionSynchronizationManager.getTransaction();
            }
            // 其他传播行为处理...
        }

        Transaction transaction = new JdbcTransactionFactory(dataSource)
                .newTransaction(dataSource, convertIsolationLevel(transactional.isolation()));
        transaction.getConnection().setAutoCommit(false);
        TransactionSynchronizationManager.bindTransaction(transaction);
        log.debug("Began transaction: {}", transaction);
        return transaction;
    }

    private void commitTransaction(Transaction transaction) throws SQLException {
        if (transaction != null && transaction.isActive() &&
                transaction == TransactionSynchronizationManager.getTransaction()) {
            transaction.commit();
            log.debug("Committed transaction: {}", transaction);
        }
    }

    private void rollbackTransaction(Transaction transaction, Exception ex) throws SQLException {
        if (transaction != null && transaction.isActive()) {
            log.warn("Rolling back transaction due to: {}", ex.getMessage());
            transaction.rollback();
        }
    }

    private void cleanupTransaction(Transaction transaction) throws SQLException {
        if (transaction != null && transaction == TransactionSynchronizationManager.getTransaction()) {
            TransactionSynchronizationManager.clear();
            transaction.close();
            log.debug("Closed transaction: {}", transaction);
        }
    }

    private TransactionIsolationLevel convertIsolationLevel(Isolation isolation) {
        switch (isolation) {
            case READ_UNCOMMITTED:
                return TransactionIsolationLevel.READ_UNCOMMITTED;
            case READ_COMMITTED:
                return TransactionIsolationLevel.READ_COMMITTED;
            case REPEATABLE_READ:
                return TransactionIsolationLevel.REPEATABLE_READ;
            case SERIALIZABLE:
                return TransactionIsolationLevel.SERIALIZABLE;
            default:
                return null;
        }
    }
}
